package com.qibao.toolkit.exception.handler;

import com.qibao.toolkit.web.ResponseModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author wangcheng
 * @version $Id: GlobalErrorController.java, v0.1 2019/5/9 17:22 wangcheng Exp $$
 */
public class GlobalErrorController extends AbstractErrorController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public static final String DEFAULT_GLOBAL_ERROR_HANDLE_BEAN_NAME = "globalErrorHandler";

    private final ErrorProperties errorProperties;

    public GlobalErrorController() {
        this(new DefaultErrorAttributes(), new ErrorProperties());
    }

    public GlobalErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        this(errorAttributes, errorProperties, Collections.<ErrorViewResolver> emptyList());
    }

    public GlobalErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers);
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
    }


    @Override
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }


    @RequestMapping("${server.error.path:${error.path:/error}}")
    public ResponseModel error(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        // 将Http返回状态都设置为200
        response.setStatus(HttpServletResponse.SC_OK);
        Integer status = getErrorStatusCode(request, response);

        logger.warn("/error -> status: {}, body: {}", status, body);

        if (body != null && body.get("message") != null) {
            return ResponseModel.ERROR(status, String.valueOf(body.get("message")));
        }
        switch (status) {
            case 400:
                return ResponseModel.ERROR(400, "error.param.bad");
            case 401:
                return ResponseModel.ERROR(401, "error.user.unauthorized");
            case 403:
                return ResponseModel.ACCESS_FORBIDDEN();
            case 404:
                return ResponseModel.NOT_FOUND();
        }

        return ResponseModel.ERROR();
    }
    protected Integer getErrorStatusCode(HttpServletRequest request, HttpServletResponse response) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            statusCode = response.getStatus();
        }
        if (statusCode == 0 || statusCode == HttpStatus.OK.value()) {
            statusCode = HttpStatus.INTERNAL_SERVER_ERROR.value();
        }
        return statusCode;
    }

    protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
        IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
        if (include == IncludeStacktrace.ALWAYS) {
            return true;
        }
        if (include == IncludeStacktrace.ON_TRACE_PARAM) {
            return getTraceParameter(request);
        }
        return false;
    }

    public ErrorProperties getErrorProperties() {
        return errorProperties;
    }
}
